// 1 <= C <= 1000
// 1 <= M, N, a, b, m, seed0 <= 20000

#include "pthread.h"
#include <stdio.h>
#include <string.h>


#if defined(KOROL_TEST)
   const int MAX_COLUMNS_NUMBER = 10000;
#else
   const int MAX_COLUMNS_NUMBER = 20000;
#endif
const int DATA_SIZE = MAX_COLUMNS_NUMBER * (MAX_COLUMNS_NUMBER + 1); // don't forget space for "zero" column
const int MAX_CONCURRENT_CASES = 20;
const int MAX_THREAD_NUMBER = 40;

#if defined(WIN32) || defined(WIN64)
    #include <windows.h>
    #pragma comment(lib, "kernel32.lib")
#else /* !defined(WIN32) && !defined(WIN64) */
    #include <unistd.h>
#endif /* defined(WIN32) || defined(WIN64) */


template<typename T>
class TSStack
{
public:
    TSStack() : m_count(0)
    {
        pthread_spin_init(&m_lock, 0);
    }

    void push(T& element)
    {
        pthread_spin_lock(&m_lock);
        m_storage[m_count] = element;
        ++m_count;
        pthread_spin_unlock(&m_lock);
    }

    bool pop(T& element)
    {
        pthread_spin_lock(&m_lock);
        if (m_count == 0)
        {
            pthread_spin_unlock(&m_lock);
            return false;
        }
        element = m_storage[m_count - 1];
        --m_count;
        pthread_spin_unlock(&m_lock);
        return true;
    }
private:
    T m_storage[3000];
    int m_count;
    pthread_spinlock_t m_lock;
};


unsigned int GetNumberOfCores(void)
{
    unsigned int numberOfCores = 1;

#if defined(WIN32) || defined(WIN64)

    SYSTEM_INFO sysInfo;

    GetSystemInfo(&sysInfo);

    numberOfCores = sysInfo.dwNumberOfProcessors;

#else /* !defined(WIN32) && !defined(WIN64) */

#ifdef _SC_NPROCESSORS_CONF
    numberOfCores = sysconf(_SC_NPROCESSORS_CONF);
#endif /* _SC_NPROCESSORS_CONF */
    if (1 > numberOfCores)
    {
        numberOfCores = 1;
    }

#endif /* defined(WIN32) || defined(WIN64) */

    return numberOfCores;
}

#define POS_ITER(currMaxt, to, from, res)\
currMaxt.m_sum += *to - *from;\
if (currMaxt.m_sum < 0)\
{\
   currMaxt.m_sum = 0;\
   currMaxt.m_beginPtr = to + 1;\
}\
else if (currMaxt.m_sum > res.m_sum)\
{\
   res = currMaxt;\
   res.m_endPtr = to;\
}


#define NEG_ITER(currMaxt, to, from, res)\
currMaxt.m_sum += *to - *from;\
if (currMaxt.m_sum > res.m_sum)\
{\
   res = currMaxt;\
   res.m_endPtr = to;\
}\
if (currMaxt.m_sum < 0)\
{\
   currMaxt.m_sum = 0;\
   currMaxt.m_beginPtr = to + 1;\
}

struct Task;

typedef void (*TaskFunc)(int, Task&) ; // input int parameter - thread number

void initialTask(int, Task&);

struct ResultStruct
{
    ResultStruct(): m_ulColumn(-1), m_ulRow(-1), m_brColumn(-1), m_brRow(-1), m_sum(-2000000000)
    {}
    ResultStruct(short ulColumn, short ulRow, short brColumn, short brRow, short sum): m_ulColumn(ulColumn), m_ulRow(ulRow), m_brColumn(brColumn), m_brRow(brRow), m_sum(sum)
    {
    }
    short m_ulColumn; // Upper Left Column
    short m_ulRow;    // Upper Left Row
    short m_brColumn; // Bottom Right Column
    short m_brRow;    // Bottom Right Row
    int m_sum;
};

struct TempResultStruct
{
    TempResultStruct(int* beginPtr, int* endPtr, int width, int sum)
      : m_beginPtr(beginPtr)
      , m_endPtr(endPtr)
      , m_sum(sum)
      , m_width(width)
    {}
    int* m_beginPtr;
    int* m_endPtr;
    int m_sum;
    int m_width;
};


struct TestCaseData
{
    int M, N, a, b, m, seed0;
    int matrixNumber; //in matrixes array
    pthread_spinlock_t m_lock;
    int countedColumnSet;
    bool isTrans;
};

struct Task
{
    enum TaskType
    {
        COMBINED,
        PAIRED,
        SINGLE,
        PAIRED_COMBINED,
    };
    TaskType m_columnTaskType;
    TaskFunc m_func;
    short m_columns[10];
    short m_testCase;
    unsigned char m_columnsCount;
};

struct ThreadContext
{
    int m_number;
    TSStack<Task> m_tasks;
    ResultStruct m_testCasesResults[1000];
};

ThreadContext threadsContext[MAX_THREAD_NUMBER];
unsigned int numCores = 0;
bool workDone = false;
int *matrixes[MAX_CONCURRENT_CASES] = {0};
int concurrentCases=1;
int T;// number of test cases
int casesLaunched = 0;
int casesDone = 0;
TestCaseData testCases[1000];
pthread_spinlock_t taskCountLock;
pthread_spinlock_t taskCountDoneLock;

void printMatrix(int* mat, int m, int n)
{
    for (int i = 0; i < 20; ++i)
    {
        for (int j = 0; j < 20; ++j)
        {
            printf("%5.d", mat[j * m + i]);
        }
        printf("\n");
    }
    printf("\n");
}

void calcColumn(int * mat, int M, int fromIndex, int width, ResultStruct & result)
{
    int * to = &mat[(fromIndex + width - 1) * M];
    int *orig = to + M;
    int* from = &mat[(fromIndex - 1) * M];
    //fromIndex, 0, fromIndex + width - 1, 0, 0);
    TempResultStruct currMax(to, to, width, 0);
    TempResultStruct res(0, 0, 0, result.m_sum);
    if (res.m_sum > 0)
    {
       for (; to < orig; ++from, ++to)
       {
           POS_ITER(currMax, to, from, res)
       }
    }
    else
    {
       for (; to < orig; ++from, ++to)
       {
           NEG_ITER(currMax, to, from, res)
       }
    }
    if (res.m_sum > result.m_sum)
    {
       result.m_sum = res.m_sum;
       result.m_brColumn = (res.m_beginPtr - mat) / M;
       result.m_ulColumn = result.m_brColumn - res.m_width + 1;
       result.m_ulRow = (res.m_beginPtr - mat) % M;
       result.m_brRow = (res.m_endPtr - mat) % M;
    }
}

void calcColumnPaired2(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int * to1 = &mat[(fromIndex + width[0] - 1) * M];
   int *orig = to1 + M;
   int * to2 = &mat[(fromIndex + width[1] - 1) * M];
   int* from = &mat[(fromIndex - 1) * M];
   TempResultStruct currMax1(to1, to1, width[0], 0);
   TempResultStruct currMax2(to2, to2, width[1], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; to1 < orig; ++from, ++to1, ++to2)
      {
         POS_ITER(currMax1, to1, from, res)
         POS_ITER(currMax2, to2, from, res)
      }
   }
   else
   {
      for (;  to1 < orig; ++from, ++to1, ++to2)
      {
         NEG_ITER(currMax1, to1, from, res)
         NEG_ITER(currMax2, to2, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat) % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}

void calcColumnPaired3(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int * to1 = &mat[(fromIndex + width[0] - 1) * M];
   int * orig = to1 + M;
   int * to2 = &mat[(fromIndex + width[1] - 1) * M];
   int * to3 = &mat[(fromIndex + width[2] - 1) * M];
   int* from = &mat[(fromIndex - 1) * M];
   TempResultStruct currMax1(to1, to1, width[0], 0);
   TempResultStruct currMax2(to2, to2, width[1], 0);
   TempResultStruct currMax3(to3, to3, width[2], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; to1 < orig; ++from, ++to1, ++to2, ++to3)
      {
         POS_ITER(currMax1, to1, from, res)
         POS_ITER(currMax2, to2, from, res)
         POS_ITER(currMax3, to3, from, res)
      }
   }
   else
   {
      for (; to1 < orig; ++from, ++to1, ++to2, ++to3)
      {
         NEG_ITER(currMax1, to1, from, res)
         NEG_ITER(currMax2, to2, from, res)
         NEG_ITER(currMax3, to3, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}


void calcColumnPaired4(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
    int * to1 = &mat[(fromIndex + width[0] - 1) * M];
    int * orig = to1 + M;
    int * to2 = &mat[(fromIndex + width[1] - 1) * M];
    int * to3 = &mat[(fromIndex + width[2] - 1) * M];
    int * to4 = &mat[(fromIndex + width[3] - 1) * M];    
    int* from = &mat[(fromIndex - 1) * M];
    TempResultStruct currMax1(to1, to1, width[0], 0);
    TempResultStruct currMax2(to2, to2, width[1], 0);
    TempResultStruct currMax3(to3, to3, width[2], 0);
    TempResultStruct currMax4(to4, to4, width[3], 0);
    TempResultStruct res(0, 0, 0, result.m_sum);
    if (res.m_sum > 0)
    {
        for (; to1 < orig; ++from, ++to1, ++to2, ++to3, ++to4) 
        {
            POS_ITER(currMax1, to1, from, res)
            POS_ITER(currMax2, to2, from, res)
            POS_ITER(currMax3, to3, from, res)
            POS_ITER(currMax4, to4, from, res)
        }
    }
    else
    {
        for (; to1 < orig; ++from, ++to1, ++to2, ++to3, ++to4)
        {
           NEG_ITER(currMax1, to1, from, res)
           NEG_ITER(currMax2, to2, from, res)
           NEG_ITER(currMax3, to3, from, res)
           NEG_ITER(currMax4, to4, from, res)
        }
    }
    if (res.m_sum > result.m_sum)
    {
       result.m_sum = res.m_sum;
       result.m_brColumn = (res.m_beginPtr - mat) / M;
       result.m_ulColumn = result.m_brColumn - res.m_width + 1;
       result.m_ulRow = (res.m_beginPtr - mat)  % M;
       result.m_brRow = (res.m_endPtr - mat)  % M;
    }
}

void calcColumnPaired5(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int * to1 = &mat[(fromIndex + width[0] - 1) * M];
   int * orig = to1 + M;
   int * to2 = &mat[(fromIndex + width[1] - 1) * M];
   int * to3 = &mat[(fromIndex + width[2] - 1) * M];
   int * to4 = &mat[(fromIndex + width[3] - 1) * M];    
   int * to5 = &mat[(fromIndex + width[4] - 1) * M];    
   int* from = &mat[(fromIndex - 1) * M];
   TempResultStruct currMax1(to1, to1, width[0], 0);
   TempResultStruct currMax2(to2, to2, width[1], 0);
   TempResultStruct currMax3(to3, to3, width[2], 0);
   TempResultStruct currMax4(to4, to4, width[3], 0);
   TempResultStruct currMax5(to5, to5, width[4], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; to1 < orig; ++from, ++to1, ++to2, ++to3, ++to4, ++to5)
      {
         POS_ITER(currMax1, to1, from, res)
         POS_ITER(currMax2, to2, from, res)
         POS_ITER(currMax3, to3, from, res)
         POS_ITER(currMax4, to4, from, res)
         POS_ITER(currMax5, to5, from, res)
       }
   }
   else
   {
      for (; to1 < orig; ++from, ++to1, ++to2, ++to3, ++to4, ++to5)
      {
         NEG_ITER(currMax1, to1, from, res)
         NEG_ITER(currMax2, to2, from, res)
         NEG_ITER(currMax3, to3, from, res)
         NEG_ITER(currMax4, to4, from, res)
         NEG_ITER(currMax5, to5, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}


void calcColumnPaired6(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int * to1 = &mat[(fromIndex + width[0] - 1) * M];
   int * orig = to1 + M;
   int * to2 = &mat[(fromIndex + width[1] - 1) * M];
   int * to3 = &mat[(fromIndex + width[2] - 1) * M];
   int * to4 = &mat[(fromIndex + width[3] - 1) * M];    
   int * to5 = &mat[(fromIndex + width[4] - 1) * M];    
   int * to6 = &mat[(fromIndex + width[5] - 1) * M];    
   int* from = &mat[(fromIndex - 1) * M];
   TempResultStruct currMax1(to1, to1, width[0], 0);
   TempResultStruct currMax2(to2, to2, width[1], 0);
   TempResultStruct currMax3(to3, to3, width[2], 0);
   TempResultStruct currMax4(to4, to4, width[3], 0);
   TempResultStruct currMax5(to5, to5, width[4], 0);
   TempResultStruct currMax6(to6, to6, width[5], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; to1 < orig; ++from, ++to1, ++to2, ++to3, ++to4, ++to5, ++to6)
      {
         POS_ITER(currMax1, to1, from, res)
         POS_ITER(currMax2, to2, from, res)
         POS_ITER(currMax3, to3, from, res)
         POS_ITER(currMax4, to4, from, res)
         POS_ITER(currMax5, to5, from, res)
         POS_ITER(currMax6, to6, from, res)
      }
   }
   else
   {
      for (; to1 < orig; ++from, ++to1, ++to2, ++to3, ++to4, ++to5, ++to6)
      {
         NEG_ITER(currMax1, to1, from, res)
         NEG_ITER(currMax2, to2, from, res)
         NEG_ITER(currMax3, to3, from, res)
         NEG_ITER(currMax4, to4, from, res)
         NEG_ITER(currMax5, to5, from, res)
         NEG_ITER(currMax6, to6, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}

void calcColumnPairedTo2(int * mat, int M, int toIndex, short* width, ResultStruct & result)
{
   int * from1 = &mat[(toIndex - width[0]) * M];
   int *orig = from1 + M;
   int * from2 = &mat[(toIndex - width[1]) * M];
   int* to = &mat[toIndex * M];
   TempResultStruct currMax1(to, to, width[0], 0);
   TempResultStruct currMax2(to, to, width[1], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; from1 < orig; ++to, ++from1, ++from2)
      {
         POS_ITER(currMax1, to, from1, res);
         POS_ITER(currMax2, to, from2, res);
      }
   }
   else
   {
      for (; from1 < orig; ++to, ++from1, ++from2)
      {
         NEG_ITER(currMax1, to, from1, res);
         NEG_ITER(currMax2, to, from2, res);
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat) % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}

void calcColumnPairedTo3(int * mat, int M, int toIndex, short* width, ResultStruct & result)
{
   int* to = &mat[toIndex * M];
   int * from1 = &mat[(toIndex - width[0]) * M];
   int * from2 = &mat[(toIndex - width[1]) * M];
   int * from3 = &mat[(toIndex - width[2]) * M];
   int *orig = from1 + M;
   TempResultStruct currMax1(to, to, width[0], 0);
   TempResultStruct currMax2(to, to, width[1], 0);
   TempResultStruct currMax3(to, to, width[2], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; from1 < orig; ++to, ++from1, ++from2, ++from3)
      {
         POS_ITER(currMax1, to, from1, res);
         POS_ITER(currMax2, to, from2, res);
         POS_ITER(currMax3, to, from3, res);
      }
   }
   else
   {
      for (; from1 < orig; ++to, ++from1, ++from2, ++from3)
      {
         NEG_ITER(currMax1, to, from1, res);
         NEG_ITER(currMax2, to, from2, res);
         NEG_ITER(currMax3, to, from3, res);
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat) % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}

void calcColumnPairedTo4(int * mat, int M, int toIndex, short* width, ResultStruct & result)
{
   int* to = &mat[toIndex * M];
   int * from1 = &mat[(toIndex - width[0]) * M];
   int * from2 = &mat[(toIndex - width[1]) * M];
   int * from3 = &mat[(toIndex - width[2]) * M];
   int * from4 = &mat[(toIndex - width[3]) * M];
   int *orig = from1 + M;
   TempResultStruct currMax1(to, to, width[0], 0);
   TempResultStruct currMax2(to, to, width[1], 0);
   TempResultStruct currMax3(to, to, width[2], 0);
   TempResultStruct currMax4(to, to, width[3], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; from1 < orig; ++to, ++from1, ++from2, ++from3, ++from4)
      {
         POS_ITER(currMax1, to, from1, res);
         POS_ITER(currMax2, to, from2, res);
         POS_ITER(currMax3, to, from3, res);
         POS_ITER(currMax4, to, from4, res);
      }
   }
   else
   {
      for (; from1 < orig; ++to, ++from1, ++from2, ++from3, ++from4)
      {
         NEG_ITER(currMax1, to, from1, res);
         NEG_ITER(currMax2, to, from2, res);
         NEG_ITER(currMax3, to, from3, res);
         NEG_ITER(currMax4, to, from4, res);
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat) % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}

void calcColumnPairedTo5(int * mat, int M, int toIndex, short* width, ResultStruct & result)
{
   int* to = &mat[toIndex * M];
   int * from1 = &mat[(toIndex - width[0]) * M];
   int * from2 = &mat[(toIndex - width[1]) * M];
   int * from3 = &mat[(toIndex - width[2]) * M];
   int * from4 = &mat[(toIndex - width[3]) * M];
   int * from5 = &mat[(toIndex - width[4]) * M];
   int *orig = from1 + M;
   TempResultStruct currMax1(to, to, width[0], 0);
   TempResultStruct currMax2(to, to, width[1], 0);
   TempResultStruct currMax3(to, to, width[2], 0);
   TempResultStruct currMax4(to, to, width[3], 0);
   TempResultStruct currMax5(to, to, width[4], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; from1 < orig; ++to, ++from1, ++from2, ++from3, ++from4, ++from5)
      {
         POS_ITER(currMax1, to, from1, res);
         POS_ITER(currMax2, to, from2, res);
         POS_ITER(currMax3, to, from3, res);
         POS_ITER(currMax4, to, from4, res);
         POS_ITER(currMax5, to, from5, res);
      }
   }
   else
   {
      for (; from1 < orig; ++to, ++from1, ++from2, ++from3, ++from4, ++from5)
      {
         NEG_ITER(currMax1, to, from1, res);
         NEG_ITER(currMax2, to, from2, res);
         NEG_ITER(currMax3, to, from3, res);
         NEG_ITER(currMax4, to, from4, res);
         NEG_ITER(currMax5, to, from5, res);
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat) % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}


void calcColumnCombined2(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int * to = &mat[(fromIndex + width[0] + width[1] - 1) * M];
   int * orig = to + M;
   int* from = &mat[(fromIndex - 1) * M];
   int* middle = &mat[(fromIndex + width[0] - 1) * M];
   TempResultStruct currMax1(middle, middle, width[0], 0);
   TempResultStruct currMax2(to, to, width[1], 0);
   TempResultStruct currMax3(to, to, width[0] + width[1], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; to < orig; ++from, ++to, ++middle)
      {
         POS_ITER(currMax1, middle, from, res)
         POS_ITER(currMax2, to, middle, res)
         POS_ITER(currMax3, to, from, res)
      }
   }
   else
   {
      for (; to < orig; ++from, ++to, ++middle)
      {
         NEG_ITER(currMax1, middle, from, res)
         NEG_ITER(currMax2, to, middle, res)
         NEG_ITER(currMax3, to, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}


void calcColumnCombined3(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
    int * to = &mat[(fromIndex + width[0] + width[1] + width[2] - 1) * M];
    int * orig = to + M;
    int* from = &mat[(fromIndex - 1) * M];
    int* middle1 = &mat[(fromIndex + width[0] - 1) * M];
    int* middle2 = &mat[(fromIndex + width[0] + width[1] - 1) * M];
    TempResultStruct currMax1(middle1, middle1, width[0], 0);
    TempResultStruct currMax2(middle2, middle2, width[1], 0);
    TempResultStruct currMax3(middle2, middle2, width[0] + width[1], 0);
    TempResultStruct currMax4(to, to, width[2], 0);
    TempResultStruct currMax5(to, to, width[1] + width[2], 0);
    TempResultStruct currMax6(to, to, width[0] + width[1] + width[2], 0);
    TempResultStruct res(0, 0, 0, result.m_sum);
    if (res.m_sum > 0)
    {
        for (; to < orig; ++from, ++to, ++middle1, ++middle2)
        {
            POS_ITER(currMax1, middle1, from, res)
            POS_ITER(currMax2, middle2, middle1, res)
            POS_ITER(currMax3, middle2, from, res)
            POS_ITER(currMax4, to, middle2, res)
            POS_ITER(currMax5, to, middle1, res)
            POS_ITER(currMax6, to, from, res)
        }
    }
    else
    {
       for (; to < orig; ++from, ++to, ++middle1, ++middle2)
       {
          NEG_ITER(currMax1, middle1, from, res)
          NEG_ITER(currMax2, middle2, middle1, res)
          NEG_ITER(currMax3, middle2, from, res)
          NEG_ITER(currMax4, to, middle2, res)
          NEG_ITER(currMax5, to, middle1, res)
          NEG_ITER(currMax6, to, from, res)
       }
    }
    if (res.m_sum > result.m_sum)
    {
       result.m_sum = res.m_sum;
       result.m_brColumn = (res.m_beginPtr - mat) / M;
       result.m_ulColumn = result.m_brColumn - res.m_width + 1;
       result.m_ulRow = (res.m_beginPtr - mat)  % M;
       result.m_brRow = (res.m_endPtr - mat)  % M;
    }
}

void calcColumnCombined4(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int* from = &mat[(fromIndex - 1) * M];
   int* middle1 = &mat[(fromIndex + width[0] - 1) * M];
   int* middle2 = &mat[(fromIndex + width[0] + width[1] - 1) * M];
   int* middle3 = &mat[(fromIndex + width[0] + width[1] + width[2] - 1) * M];
   int* to = &mat[(fromIndex + width[0] + width[1] + width[2] +width[3] - 1) * M];
   int * orig = to + M;
   TempResultStruct currMax1(middle1, middle1, width[0], 0);
   TempResultStruct currMax2(middle2, middle2, width[1], 0);
   TempResultStruct currMax3(middle2, middle2, width[0] + width[1], 0);
   TempResultStruct currMax4(middle3, middle3, width[2], 0);
   TempResultStruct currMax5(middle3, middle3, width[1] + width[2], 0);
   TempResultStruct currMax6(middle3, middle3, width[0] + width[1] + width[2], 0);
   TempResultStruct currMax7(to, to, width[3], 0);
   TempResultStruct currMax8(to, to, width[2] + width[3], 0);
   TempResultStruct currMax9(to, to, width[1] + width[2] + width[3], 0);
   TempResultStruct currMax10(to, to, width[0] + width[1] + width[2] + width[3], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; to < orig; ++from, ++to, ++middle1, ++middle2, ++middle3)
      {
         POS_ITER(currMax1, middle1, from, res)
         POS_ITER(currMax2, middle2, middle1, res)
         POS_ITER(currMax3, middle2, from, res)
         POS_ITER(currMax4, middle3, middle2, res)
         POS_ITER(currMax5, middle3, middle1, res)
         POS_ITER(currMax6, middle3, from, res)
         POS_ITER(currMax7, to, middle3, res)
         POS_ITER(currMax8, to, middle2, res)
         POS_ITER(currMax9, to, middle1, res)
         POS_ITER(currMax10, to, from, res)
      }
   }
   else
   {
      for (; to < orig; ++from, ++to, ++middle1, ++middle2, ++middle3)
      {
         NEG_ITER(currMax1, middle1, from, res)
         NEG_ITER(currMax2, middle2, middle1, res)
         NEG_ITER(currMax3, middle2, from, res)
         NEG_ITER(currMax4, middle3, middle2, res)
         NEG_ITER(currMax5, middle3, middle1, res)
         NEG_ITER(currMax6, middle3, from, res)
         NEG_ITER(currMax7, to, middle3, res)
         NEG_ITER(currMax8, to, middle2, res)
         NEG_ITER(currMax9, to, middle1, res)
         NEG_ITER(currMax10, to, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}

void calcColumnCombined5(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int* from = &mat[(fromIndex - 1) * M];
   int* middle1 = &mat[(fromIndex + width[0] - 1) * M];
   int* middle2 = &mat[(fromIndex + width[0] + width[1] - 1) * M];
   int* middle3 = &mat[(fromIndex + width[0] + width[1] + width[2] - 1) * M];
   int* middle4 = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] - 1) * M];
   int* to = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] + width[4] - 1) * M];
   int* orig = to + M;
   TempResultStruct currMax1(middle1, middle1, width[0], 0);
   TempResultStruct currMax2(middle2, middle2, width[1], 0);
   TempResultStruct currMax3(middle2, middle2, width[0] + width[1], 0);
   TempResultStruct currMax4(middle3, middle3, width[2], 0);
   TempResultStruct currMax5(middle3, middle3, width[1] + width[2], 0);
   TempResultStruct currMax6(middle3, middle3, width[0] + width[1] + width[2], 0);
   TempResultStruct currMax7(middle4, middle4, width[3], 0);
   TempResultStruct currMax8(middle4, middle4, width[2] + width[3], 0);
   TempResultStruct currMax9(middle4, middle4, width[1] + width[2] + width[3], 0);
   TempResultStruct currMax10(middle4, middle4, width[0] + width[1] + width[2] + width[3], 0);
   TempResultStruct currMax11(to, to, width[4], 0);
   TempResultStruct currMax12(to, to, width[3] + width[4], 0);
   TempResultStruct currMax13(to, to, width[2] + width[3] + width[4], 0);
   TempResultStruct currMax14(to, to, width[1] + width[2] + width[3] + width[4], 0);
   TempResultStruct currMax15(to, to, width[0] +width[1] + width[2] + width[3] + width[4], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; to < orig; ++from, ++to, ++middle1, ++middle2, ++middle3, ++middle4)
      {
         POS_ITER(currMax1, middle1, from, res)
         POS_ITER(currMax2, middle2, middle1, res)
         POS_ITER(currMax3, middle2, from, res)
         POS_ITER(currMax4, middle3, middle2, res)
         POS_ITER(currMax5, middle3, middle1, res)
         POS_ITER(currMax6, middle3, from, res)
         POS_ITER(currMax7, middle4, middle3, res)
         POS_ITER(currMax8, middle4, middle2, res)
         POS_ITER(currMax9, middle4, middle1, res)
         POS_ITER(currMax10, middle4, from, res)
         POS_ITER(currMax11, to, middle4, res)
         POS_ITER(currMax12, to, middle3, res)
         POS_ITER(currMax13, to, middle2, res)
         POS_ITER(currMax14, to, middle1, res)
         POS_ITER(currMax15, to, from, res)
      }
   }
   else
   {
      for (; to < orig; ++from, ++to, ++middle1, ++middle2, ++middle3, ++middle4)
      {
         NEG_ITER(currMax1, middle1, from, res)
         NEG_ITER(currMax2, middle2, middle1, res)
         NEG_ITER(currMax3, middle2, from, res)
         NEG_ITER(currMax4, middle3, middle2, res)
         NEG_ITER(currMax5, middle3, middle1, res)
         NEG_ITER(currMax6, middle3, from, res)
         NEG_ITER(currMax7, middle4, middle3, res)
         NEG_ITER(currMax8, middle4, middle2, res)
         NEG_ITER(currMax9, middle4, middle1, res)
         NEG_ITER(currMax10, middle4, from, res)
         NEG_ITER(currMax11, to, middle4, res)
         NEG_ITER(currMax12, to, middle3, res)
         NEG_ITER(currMax13, to, middle2, res)
         NEG_ITER(currMax14, to, middle1, res)
         NEG_ITER(currMax15, to, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}

void calcColumnCombined6(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int* from = &mat[(fromIndex - 1) * M];
   int* middle1 = &mat[(fromIndex + width[0] - 1) * M];
   int* middle2 = &mat[(fromIndex + width[0] + width[1] - 1) * M];
   int* middle3 = &mat[(fromIndex + width[0] + width[1] + width[2] - 1) * M];
   int* middle4 = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] - 1) * M];
   int* middle5 = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] + width[4] - 1) * M];
   int* to = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] + width[4] + width[5] - 1) * M];
   int* orig = to + M;
   TempResultStruct currMax1(middle1, middle1, width[0], 0);
   TempResultStruct currMax2(middle2, middle2, width[1], 0);
   TempResultStruct currMax3(middle2, middle2, width[0] + width[1], 0);
   TempResultStruct currMax4(middle3, middle3, width[2], 0);
   TempResultStruct currMax5(middle3, middle3, width[1] + width[2], 0);
   TempResultStruct currMax6(middle3, middle3, width[0] + width[1] + width[2], 0);
   TempResultStruct currMax7(middle4, middle4, width[3], 0);
   TempResultStruct currMax8(middle4, middle4, width[2] + width[3], 0);
   TempResultStruct currMax9(middle4, middle4, width[1] + width[2] + width[3], 0);
   TempResultStruct currMax10(middle4, middle4, width[0] + width[1] + width[2] + width[3], 0);
   TempResultStruct currMax11(middle5, middle5, width[4], 0);
   TempResultStruct currMax12(middle5, middle5, width[3] + width[4], 0);
   TempResultStruct currMax13(middle5, middle5, width[2] + width[3] + width[4], 0);
   TempResultStruct currMax14(middle5, middle5, width[1] + width[2] + width[3] + width[4], 0);
   TempResultStruct currMax15(middle5, middle5, width[0] +width[1] + width[2] + width[3] + width[4], 0);
   TempResultStruct currMax16(to, to, width[5], 0);
   TempResultStruct currMax17(to, to, width[4] + width[5], 0);
   TempResultStruct currMax18(to, to, width[3] + width[4] + width[5], 0);
   TempResultStruct currMax19(to, to, width[2] + width[3] + width[4] + width[5], 0);
   TempResultStruct currMax20(to, to, width[1] + width[2] + width[3] + width[4] + width[5], 0);
   TempResultStruct currMax21(to, to, width[0] +width[1] + width[2] + width[3] + width[4] + width[5], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; to < orig; ++from, ++to, ++middle1, ++middle2, ++middle3, ++middle4, ++middle5)
      {
         POS_ITER(currMax1, middle1, from, res)
         POS_ITER(currMax2, middle2, middle1, res)
         POS_ITER(currMax3, middle2, from, res)
         POS_ITER(currMax4, middle3, middle2, res)
         POS_ITER(currMax5, middle3, middle1, res)
         POS_ITER(currMax6, middle3, from, res)
         POS_ITER(currMax7, middle4, middle3, res)
         POS_ITER(currMax8, middle4, middle2, res)
         POS_ITER(currMax9, middle4, middle1, res)
         POS_ITER(currMax10, middle4, from, res)
         POS_ITER(currMax11, middle5, middle4, res)
         POS_ITER(currMax12, middle5, middle3, res)
         POS_ITER(currMax13, middle5, middle2, res)
         POS_ITER(currMax14, middle5, middle1, res)
         POS_ITER(currMax15, middle5, from, res)
         POS_ITER(currMax16, to, middle5, res)
         POS_ITER(currMax17, to, middle4, res)
         POS_ITER(currMax18, to, middle3, res)
         POS_ITER(currMax19, to, middle2, res)
         POS_ITER(currMax20, to, middle1, res)
         POS_ITER(currMax21, to, from, res)
      }
   }
   else
   {
      for (; to < orig; ++from, ++to, ++middle1, ++middle2, ++middle3, ++middle4, ++middle5)
      {
         NEG_ITER(currMax1, middle1, from, res)
         NEG_ITER(currMax2, middle2, middle1, res)
         NEG_ITER(currMax3, middle2, from, res)
         NEG_ITER(currMax4, middle3, middle2, res)
         NEG_ITER(currMax5, middle3, middle1, res)
         NEG_ITER(currMax6, middle3, from, res)
         NEG_ITER(currMax7, middle4, middle3, res)
         NEG_ITER(currMax8, middle4, middle2, res)
         NEG_ITER(currMax9, middle4, middle1, res)
         NEG_ITER(currMax10, middle4, from, res)
         NEG_ITER(currMax11, middle5, middle4, res)
         NEG_ITER(currMax12, middle5, middle3, res)
         NEG_ITER(currMax13, middle5, middle2, res)
         NEG_ITER(currMax14, middle5, middle1, res)
         NEG_ITER(currMax15, middle5, from, res)
         NEG_ITER(currMax16, to, middle5, res)
         NEG_ITER(currMax17, to, middle4, res)
         NEG_ITER(currMax18, to, middle3, res)
         NEG_ITER(currMax19, to, middle2, res)
         NEG_ITER(currMax20, to, middle1, res)
         NEG_ITER(currMax21, to, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}

void calcColumnCombined7(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int* from = &mat[(fromIndex - 1) * M];
   int* middle1 = &mat[(fromIndex + width[0] - 1) * M];
   int* middle2 = &mat[(fromIndex + width[0] + width[1] - 1) * M];
   int* middle3 = &mat[(fromIndex + width[0] + width[1] + width[2] - 1) * M];
   int* middle4 = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] - 1) * M];
   int* middle5 = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] + width[4] - 1) * M];
   int* middle6 = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] + width[4] + width[5] - 1) * M];
   int* to = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] + width[4] + width[5] + width[6] - 1) * M];
   int* orig = to + M;
   TempResultStruct currMax1(middle1, middle1, width[0], 0);
   TempResultStruct currMax2(middle2, middle2, width[1], 0);
   TempResultStruct currMax3(middle2, middle2, width[0] + width[1], 0);
   TempResultStruct currMax4(middle3, middle3, width[2], 0);
   TempResultStruct currMax5(middle3, middle3, width[1] + width[2], 0);
   TempResultStruct currMax6(middle3, middle3, width[0] + width[1] + width[2], 0);
   TempResultStruct currMax7(middle4, middle4, width[3], 0);
   TempResultStruct currMax8(middle4, middle4, width[2] + width[3], 0);
   TempResultStruct currMax9(middle4, middle4, width[1] + width[2] + width[3], 0);
   TempResultStruct currMax10(middle4, middle4, width[0] + width[1] + width[2] + width[3], 0);
   TempResultStruct currMax11(middle5, middle5, width[4], 0);
   TempResultStruct currMax12(middle5, middle5, width[3] + width[4], 0);
   TempResultStruct currMax13(middle5, middle5, width[2] + width[3] + width[4], 0);
   TempResultStruct currMax14(middle5, middle5, width[1] + width[2] + width[3] + width[4], 0);
   TempResultStruct currMax15(middle5, middle5, width[0] +width[1] + width[2] + width[3] + width[4], 0);
   TempResultStruct currMax16(middle6, middle6, width[5], 0);
   TempResultStruct currMax17(middle6, middle6, width[4] + width[5], 0);
   TempResultStruct currMax18(middle6, middle6, width[3] + width[4] + width[5], 0);
   TempResultStruct currMax19(middle6, middle6, width[2] + width[3] + width[4] + width[5], 0);
   TempResultStruct currMax20(middle6, middle6, width[1] + width[2] + width[3] + width[4] + width[5], 0);
   TempResultStruct currMax21(middle6, middle6, width[0] +width[1] + width[2] + width[3] + width[4] + width[5], 0);
   TempResultStruct currMax22(to, to, width[6], 0);
   TempResultStruct currMax23(to, to, width[5] + width[6], 0);
   TempResultStruct currMax24(to, to, width[4] + width[5] + width[6], 0);
   TempResultStruct currMax25(to, to, width[3] + width[4] + width[5] + width[6], 0);
   TempResultStruct currMax26(to, to, width[2] + width[3] + width[4] + width[5] + width[6], 0);
   TempResultStruct currMax27(to, to, width[1] + width[2] + width[3] + width[4] + width[5] + width[6], 0);
   TempResultStruct currMax28(to, to, width[0] +width[1] + width[2] + width[3] + width[4] + width[5] + width[6], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; to < orig; ++from, ++to, ++middle1, ++middle2, ++middle3, ++middle4, ++middle5, ++middle6)
      {
         POS_ITER(currMax1, middle1, from, res)
            POS_ITER(currMax2, middle2, middle1, res)
            POS_ITER(currMax3, middle2, from, res)
            POS_ITER(currMax4, middle3, middle2, res)
            POS_ITER(currMax5, middle3, middle1, res)
            POS_ITER(currMax6, middle3, from, res)
            POS_ITER(currMax7, middle4, middle3, res)
            POS_ITER(currMax8, middle4, middle2, res)
            POS_ITER(currMax9, middle4, middle1, res)
            POS_ITER(currMax10, middle4, from, res)
            POS_ITER(currMax11, middle5, middle4, res)
            POS_ITER(currMax12, middle5, middle3, res)
            POS_ITER(currMax13, middle5, middle2, res)
            POS_ITER(currMax14, middle5, middle1, res)
            POS_ITER(currMax15, middle5, from, res)
            POS_ITER(currMax16, middle6, middle5, res)
            POS_ITER(currMax17, middle6, middle4, res)
            POS_ITER(currMax18, middle6, middle3, res)
            POS_ITER(currMax19, middle6, middle2, res)
            POS_ITER(currMax20, middle6, middle1, res)
            POS_ITER(currMax21, middle6, from, res)
            POS_ITER(currMax22, to, middle6, res)
            POS_ITER(currMax23, to, middle5, res)
            POS_ITER(currMax24, to, middle4, res)
            POS_ITER(currMax25, to, middle3, res)
            POS_ITER(currMax26, to, middle2, res)
            POS_ITER(currMax27, to, middle1, res)
            POS_ITER(currMax28, to, from, res)
      }
   }
   else
   {
      for (; to < orig; ++from, ++to, ++middle1, ++middle2, ++middle3, ++middle4, ++middle5, ++middle6)
      {
         NEG_ITER(currMax1, middle1, from, res)
            NEG_ITER(currMax2, middle2, middle1, res)
            NEG_ITER(currMax3, middle2, from, res)
            NEG_ITER(currMax4, middle3, middle2, res)
            NEG_ITER(currMax5, middle3, middle1, res)
            NEG_ITER(currMax6, middle3, from, res)
            NEG_ITER(currMax7, middle4, middle3, res)
            NEG_ITER(currMax8, middle4, middle2, res)
            NEG_ITER(currMax9, middle4, middle1, res)
            NEG_ITER(currMax10, middle4, from, res)
            NEG_ITER(currMax11, middle5, middle4, res)
            NEG_ITER(currMax12, middle5, middle3, res)
            NEG_ITER(currMax13, middle5, middle2, res)
            NEG_ITER(currMax14, middle5, middle1, res)
            NEG_ITER(currMax15, middle5, from, res)
            NEG_ITER(currMax16, middle6, middle5, res)
            NEG_ITER(currMax17, middle6, middle4, res)
            NEG_ITER(currMax18, middle6, middle3, res)
            NEG_ITER(currMax19, middle6, middle2, res)
            NEG_ITER(currMax20, middle6, middle1, res)
            NEG_ITER(currMax21, middle6, from, res)
            NEG_ITER(currMax22, to, middle6, res)
            NEG_ITER(currMax23, to, middle5, res)
            NEG_ITER(currMax24, to, middle4, res)
            NEG_ITER(currMax25, to, middle3, res)
            NEG_ITER(currMax26, to, middle2, res)
            NEG_ITER(currMax27, to, middle1, res)
            NEG_ITER(currMax28, to, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}


void calcColumnCombined8(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int* from = &mat[(fromIndex - 1) * M];
   int* middle1 = &mat[(fromIndex + width[0] - 1) * M];
   int* middle2 = &mat[(fromIndex + width[0] + width[1] - 1) * M];
   int* middle3 = &mat[(fromIndex + width[0] + width[1] + width[2] - 1) * M];
   int* middle4 = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] - 1) * M];
   int* middle5 = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] + width[4] - 1) * M];
   int* middle6 = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] + width[4] + width[5] - 1) * M];
   int* middle7 = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] + width[4] + width[5] + width[6] - 1) * M];
   int* to = &mat[(fromIndex + width[0] + width[1] + width[2] + width[3] + width[4] + width[5] + width[6] + width[7] - 1) * M];
   int* orig = to + M;
   TempResultStruct currMax1(middle1, middle1, width[0], 0);
   TempResultStruct currMax2(middle2, middle2, width[1], 0);
   TempResultStruct currMax3(middle2, middle2, width[0] + width[1], 0);
   TempResultStruct currMax4(middle3, middle3, width[2], 0);
   TempResultStruct currMax5(middle3, middle3, width[1] + width[2], 0);
   TempResultStruct currMax6(middle3, middle3, width[0] + width[1] + width[2], 0);
   TempResultStruct currMax7(middle4, middle4, width[3], 0);
   TempResultStruct currMax8(middle4, middle4, width[2] + width[3], 0);
   TempResultStruct currMax9(middle4, middle4, width[1] + width[2] + width[3], 0);
   TempResultStruct currMax10(middle4, middle4, width[0] + width[1] + width[2] + width[3], 0);
   TempResultStruct currMax11(middle5, middle5, width[4], 0);
   TempResultStruct currMax12(middle5, middle5, width[3] + width[4], 0);
   TempResultStruct currMax13(middle5, middle5, width[2] + width[3] + width[4], 0);
   TempResultStruct currMax14(middle5, middle5, width[1] + width[2] + width[3] + width[4], 0);
   TempResultStruct currMax15(middle5, middle5, width[0] +width[1] + width[2] + width[3] + width[4], 0);
   TempResultStruct currMax16(middle6, middle6, width[5], 0);
   TempResultStruct currMax17(middle6, middle6, width[4] + width[5], 0);
   TempResultStruct currMax18(middle6, middle6, width[3] + width[4] + width[5], 0);
   TempResultStruct currMax19(middle6, middle6, width[2] + width[3] + width[4] + width[5], 0);
   TempResultStruct currMax20(middle6, middle6, width[1] + width[2] + width[3] + width[4] + width[5], 0);
   TempResultStruct currMax21(middle6, middle6, width[0] +width[1] + width[2] + width[3] + width[4] + width[5], 0);
   TempResultStruct currMax22(middle7, middle7, width[6], 0);
   TempResultStruct currMax23(middle7, middle7, width[5] + width[6], 0);
   TempResultStruct currMax24(middle7, middle7, width[4] + width[5] + width[6], 0);
   TempResultStruct currMax25(middle7, middle7, width[3] + width[4] + width[5] + width[6], 0);
   TempResultStruct currMax26(middle7, middle7, width[2] + width[3] + width[4] + width[5] + width[6], 0);
   TempResultStruct currMax27(middle7, middle7, width[1] + width[2] + width[3] + width[4] + width[5] + width[6], 0);
   TempResultStruct currMax28(middle7, middle7, width[0] +width[1] + width[2] + width[3] + width[4] + width[5] + width[6], 0);
   TempResultStruct currMax29(to, to, width[7], 0);
   TempResultStruct currMax30(to, to, width[6] + width[7], 0);
   TempResultStruct currMax31(to, to, width[5] + width[6] + width[7], 0);
   TempResultStruct currMax32(to, to, width[4] + width[5] + width[6] + width[7], 0);
   TempResultStruct currMax33(to, to, width[3] + width[4] + width[5] + width[6] + width[7], 0);
   TempResultStruct currMax34(to, to, width[2] + width[3] + width[4] + width[5] + width[6] + width[7], 0);
   TempResultStruct currMax35(to, to, width[1] + width[2] + width[3] + width[4] + width[5] + width[6] + width[7], 0);
   TempResultStruct currMax36(to, to, width[0] +width[1] + width[2] + width[3] + width[4] + width[5] + width[6] + width[7], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; to < orig; ++from, ++to, ++middle1, ++middle2, ++middle3, ++middle4, ++middle5, ++middle6, ++middle7)
      {
         POS_ITER(currMax1, middle1, from, res)
            POS_ITER(currMax2, middle2, middle1, res)
            POS_ITER(currMax3, middle2, from, res)
            POS_ITER(currMax4, middle3, middle2, res)
            POS_ITER(currMax5, middle3, middle1, res)
            POS_ITER(currMax6, middle3, from, res)
            POS_ITER(currMax7, middle4, middle3, res)
            POS_ITER(currMax8, middle4, middle2, res)
            POS_ITER(currMax9, middle4, middle1, res)
            POS_ITER(currMax10, middle4, from, res)
            POS_ITER(currMax11, middle5, middle4, res)
            POS_ITER(currMax12, middle5, middle3, res)
            POS_ITER(currMax13, middle5, middle2, res)
            POS_ITER(currMax14, middle5, middle1, res)
            POS_ITER(currMax15, middle5, from, res)
            POS_ITER(currMax16, middle6, middle5, res)
            POS_ITER(currMax17, middle6, middle4, res)
            POS_ITER(currMax18, middle6, middle3, res)
            POS_ITER(currMax19, middle6, middle2, res)
            POS_ITER(currMax20, middle6, middle1, res)
            POS_ITER(currMax21, middle6, from, res)
            POS_ITER(currMax22, middle7, middle6, res)
            POS_ITER(currMax23, middle7, middle5, res)
            POS_ITER(currMax24, middle7, middle4, res)
            POS_ITER(currMax25, middle7, middle3, res)
            POS_ITER(currMax26, middle7, middle2, res)
            POS_ITER(currMax27, middle7, middle1, res)
            POS_ITER(currMax28, middle7, from, res)
            POS_ITER(currMax29, to, middle7, res)
            POS_ITER(currMax30, to, middle6, res)
            POS_ITER(currMax31, to, middle5, res)
            POS_ITER(currMax32, to, middle4, res)
            POS_ITER(currMax33, to, middle3, res)
            POS_ITER(currMax34, to, middle2, res)
            POS_ITER(currMax35, to, middle1, res)
            POS_ITER(currMax36, to, from, res)
      }
   }
   else
   {
      for (; to < orig; ++from, ++to, ++middle1, ++middle2, ++middle3, ++middle4, ++middle5, ++middle6, ++middle7)
      {
         NEG_ITER(currMax1, middle1, from, res)
            NEG_ITER(currMax2, middle2, middle1, res)
            NEG_ITER(currMax3, middle2, from, res)
            NEG_ITER(currMax4, middle3, middle2, res)
            NEG_ITER(currMax5, middle3, middle1, res)
            NEG_ITER(currMax6, middle3, from, res)
            NEG_ITER(currMax7, middle4, middle3, res)
            NEG_ITER(currMax8, middle4, middle2, res)
            NEG_ITER(currMax9, middle4, middle1, res)
            NEG_ITER(currMax10, middle4, from, res)
            NEG_ITER(currMax11, middle5, middle4, res)
            NEG_ITER(currMax12, middle5, middle3, res)
            NEG_ITER(currMax13, middle5, middle2, res)
            NEG_ITER(currMax14, middle5, middle1, res)
            NEG_ITER(currMax15, middle5, from, res)
            NEG_ITER(currMax16, middle6, middle5, res)
            NEG_ITER(currMax17, middle6, middle4, res)
            NEG_ITER(currMax18, middle6, middle3, res)
            NEG_ITER(currMax19, middle6, middle2, res)
            NEG_ITER(currMax20, middle6, middle1, res)
            NEG_ITER(currMax21, middle6, from, res)
            NEG_ITER(currMax22, middle7, middle6, res)
            NEG_ITER(currMax23, middle7, middle5, res)
            NEG_ITER(currMax24, middle7, middle4, res)
            NEG_ITER(currMax25, middle7, middle3, res)
            NEG_ITER(currMax26, middle7, middle2, res)
            NEG_ITER(currMax27, middle7, middle1, res)
            NEG_ITER(currMax28, middle7, from, res)
            NEG_ITER(currMax29, to, middle7, res)
            NEG_ITER(currMax30, to, middle6, res)
            NEG_ITER(currMax31, to, middle5, res)
            NEG_ITER(currMax32, to, middle4, res)
            NEG_ITER(currMax33, to, middle3, res)
            NEG_ITER(currMax34, to, middle2, res)
            NEG_ITER(currMax35, to, middle1, res)
            NEG_ITER(currMax36, to, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}

void calcColumnPairedCombined2x2(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int* from = &mat[(fromIndex - 1) * M];
   int * orig = from + M;
   int * to1 = &mat[(fromIndex + width[0] + width[1] - 1) * M];
   int* middle1 = &mat[(fromIndex + width[0] - 1) * M];
   int * to2 = &mat[(fromIndex + width[2] + width[3] - 1) * M];
   int* middle2 = &mat[(fromIndex + width[2] - 1) * M];
   TempResultStruct currMax1(middle1, middle1, width[0], 0);
   TempResultStruct currMax2(to1, to1, width[1], 0);
   TempResultStruct currMax3(to1, to1, width[0] + width[1], 0);
   TempResultStruct currMax4(middle2, middle2, width[2], 0);
   TempResultStruct currMax5(to2, to2, width[3], 0);
   TempResultStruct currMax6(to2, to2, width[2] + width[3], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; from < orig; ++from, ++to1, ++middle1, ++to2, ++middle2)
      {
         POS_ITER(currMax1, middle1, from, res)
         POS_ITER(currMax2, to1, middle1, res)
         POS_ITER(currMax3, to1, from, res)
         POS_ITER(currMax4, middle2, from, res)
         POS_ITER(currMax5, to2, middle2, res)
         POS_ITER(currMax6, to2, from, res)
      }
   }
   else
   {
      for (; from < orig; ++from, ++to1, ++middle1, ++to2, ++middle2)
      {
         NEG_ITER(currMax1, middle1, from, res)
         NEG_ITER(currMax2, to1, middle1, res)
         NEG_ITER(currMax3, to1, from, res)
         NEG_ITER(currMax4, middle2, from, res)
         NEG_ITER(currMax5, to2, middle2, res)
         NEG_ITER(currMax6, to2, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}



void calcColumnPairedCombined2x3(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int* from = &mat[(fromIndex - 1) * M];
   int * orig = from + M;
   int * to1 = &mat[(fromIndex + width[0] + width[1] - 1) * M];
   int* middle1 = &mat[(fromIndex + width[0] - 1) * M];
   int * to2 = &mat[(fromIndex + width[2] + width[3] - 1) * M];
   int* middle2 = &mat[(fromIndex + width[2] - 1) * M];
   int * to3 = &mat[(fromIndex + width[4] + width[5] - 1) * M];
   int* middle3 = &mat[(fromIndex + width[4] - 1) * M];
   TempResultStruct currMax1(middle1, middle1, width[0], 0);
   TempResultStruct currMax2(to1, to1, width[1], 0);
   TempResultStruct currMax3(to1, to1, width[0] + width[1], 0);
   TempResultStruct currMax4(middle2, middle2, width[2], 0);
   TempResultStruct currMax5(to2, to2, width[3], 0);
   TempResultStruct currMax6(to2, to2, width[2] + width[3], 0);
   TempResultStruct currMax7(middle3, middle3, width[4], 0);
   TempResultStruct currMax8(to3, to3, width[5], 0);
   TempResultStruct currMax9(to3, to3, width[4] + width[5], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; from < orig; ++from, ++to1, ++middle1, ++to2, ++middle2, ++to3, ++middle3)
      {
         POS_ITER(currMax1, middle1, from, res)
         POS_ITER(currMax2, to1, middle1, res)
         POS_ITER(currMax3, to1, from, res)
         POS_ITER(currMax4, middle2, from, res)
         POS_ITER(currMax5, to2, middle2, res)
         POS_ITER(currMax6, to2, from, res)
         POS_ITER(currMax7, middle3, from, res)
         POS_ITER(currMax8, to3, middle3, res)
         POS_ITER(currMax9, to3, from, res)
      }
   }
   else
   {
      for (; from < orig; ++from, ++to1, ++middle1, ++to2, ++middle2, ++to3, ++middle3)
      {
         NEG_ITER(currMax1, middle1, from, res)
         NEG_ITER(currMax2, to1, middle1, res)
         NEG_ITER(currMax3, to1, from, res)
         NEG_ITER(currMax4, middle2, from, res)
         NEG_ITER(currMax5, to2, middle2, res)
         NEG_ITER(currMax6, to2, from, res)
         NEG_ITER(currMax7, middle3, from, res)
         NEG_ITER(currMax8, to3, middle3, res)
         NEG_ITER(currMax9, to3, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}

void calcColumnPairedCombined2x4(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int* from = &mat[(fromIndex - 1) * M];
   int * orig = from + M;
   int * to1 = &mat[(fromIndex + width[0] + width[1] - 1) * M];
   int* middle1 = &mat[(fromIndex + width[0] - 1) * M];
   int * to2 = &mat[(fromIndex + width[2] + width[3] - 1) * M];
   int* middle2 = &mat[(fromIndex + width[2] - 1) * M];
   int * to3 = &mat[(fromIndex + width[4] + width[5] - 1) * M];
   int* middle3 = &mat[(fromIndex + width[4] - 1) * M];
   int * to4 = &mat[(fromIndex + width[6] + width[7] - 1) * M];
   int* middle4 = &mat[(fromIndex + width[6] - 1) * M];
   TempResultStruct currMax1(middle1, middle1, width[0], 0);
   TempResultStruct currMax2(to1, to1, width[1], 0);
   TempResultStruct currMax3(to1, to1, width[0] + width[1], 0);
   TempResultStruct currMax4(middle2, middle2, width[2], 0);
   TempResultStruct currMax5(to2, to2, width[3], 0);
   TempResultStruct currMax6(to2, to2, width[2] + width[3], 0);
   TempResultStruct currMax7(middle3, middle3, width[4], 0);
   TempResultStruct currMax8(to3, to3, width[5], 0);
   TempResultStruct currMax9(to3, to3, width[4] + width[5], 0);
   TempResultStruct currMax10(middle4, middle4, width[6], 0);
   TempResultStruct currMax11(to4, to4, width[7], 0);
   TempResultStruct currMax12(to4, to4, width[6] + width[7], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; from < orig; ++from, 
            ++to1, ++middle1, 
            ++to2, ++middle2, 
            ++to3, ++middle3, 
            ++to4, ++middle4)
      {
         POS_ITER(currMax1, middle1, from, res)
         POS_ITER(currMax2, to1, middle1, res)
         POS_ITER(currMax3, to1, from, res)
         POS_ITER(currMax4, middle2, from, res)
         POS_ITER(currMax5, to2, middle2, res)
         POS_ITER(currMax6, to2, from, res)
         POS_ITER(currMax7, middle3, from, res)
         POS_ITER(currMax8, to3, middle3, res)
         POS_ITER(currMax9, to3, from, res)
         POS_ITER(currMax10, middle4, from, res)
         POS_ITER(currMax11, to4, middle4, res)
         POS_ITER(currMax12, to4, from, res)
      }
   }
   else
   {
      for (; from < orig; ++from, 
         ++to1, ++middle1, 
         ++to2, ++middle2, 
         ++to3, ++middle3, 
         ++to4, ++middle4)
      {
         NEG_ITER(currMax1, middle1, from, res)
         NEG_ITER(currMax2, to1, middle1, res)
         NEG_ITER(currMax3, to1, from, res)
         NEG_ITER(currMax4, middle2, from, res)
         NEG_ITER(currMax5, to2, middle2, res)
         NEG_ITER(currMax6, to2, from, res)
         NEG_ITER(currMax7, middle3, from, res)
         NEG_ITER(currMax8, to3, middle3, res)
         NEG_ITER(currMax9, to3, from, res)
         NEG_ITER(currMax10, middle4, from, res)
         NEG_ITER(currMax11, to4, middle4, res)
         NEG_ITER(currMax12, to4, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}

void calcColumnPairedCombined2x5(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int* from = &mat[(fromIndex - 1) * M];
   int * orig = from + M;
   int * to1 = &mat[(fromIndex + width[0] + width[1] - 1) * M];
   int* middle1 = &mat[(fromIndex + width[0] - 1) * M];
   int * to2 = &mat[(fromIndex + width[2] + width[3] - 1) * M];
   int* middle2 = &mat[(fromIndex + width[2] - 1) * M];
   int * to3 = &mat[(fromIndex + width[4] + width[5] - 1) * M];
   int* middle3 = &mat[(fromIndex + width[4] - 1) * M];
   int * to4 = &mat[(fromIndex + width[6] + width[7] - 1) * M];
   int* middle4 = &mat[(fromIndex + width[6] - 1) * M];
   int * to5 = &mat[(fromIndex + width[8] + width[9] - 1) * M];
   int* middle5 = &mat[(fromIndex + width[8] - 1) * M];
   TempResultStruct currMax1(middle1, middle1, width[0], 0);
   TempResultStruct currMax2(to1, to1, width[1], 0);
   TempResultStruct currMax3(to1, to1, width[0] + width[1], 0);
   TempResultStruct currMax4(middle2, middle2, width[2], 0);
   TempResultStruct currMax5(to2, to2, width[3], 0);
   TempResultStruct currMax6(to2, to2, width[2] + width[3], 0);
   TempResultStruct currMax7(middle3, middle3, width[4], 0);
   TempResultStruct currMax8(to3, to3, width[5], 0);
   TempResultStruct currMax9(to3, to3, width[4] + width[5], 0);
   TempResultStruct currMax10(middle4, middle4, width[6], 0);
   TempResultStruct currMax11(to4, to4, width[7], 0);
   TempResultStruct currMax12(to4, to4, width[6] + width[7], 0);
   TempResultStruct currMax13(middle5, middle5, width[8], 0);
   TempResultStruct currMax14(to5, to5, width[9], 0);
   TempResultStruct currMax15(to5, to5, width[8] + width[9], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; from < orig; ++from, 
         ++to1, ++middle1, 
         ++to2, ++middle2, 
         ++to3, ++middle3, 
         ++to4, ++middle4,
         ++to5, ++middle5)
      {
         POS_ITER(currMax1, middle1, from, res)
            POS_ITER(currMax2, to1, middle1, res)
            POS_ITER(currMax3, to1, from, res)
            POS_ITER(currMax4, middle2, from, res)
            POS_ITER(currMax5, to2, middle2, res)
            POS_ITER(currMax6, to2, from, res)
            POS_ITER(currMax7, middle3, from, res)
            POS_ITER(currMax8, to3, middle3, res)
            POS_ITER(currMax9, to3, from, res)
            POS_ITER(currMax10, middle4, from, res)
            POS_ITER(currMax11, to4, middle4, res)
            POS_ITER(currMax12, to4, from, res)
            POS_ITER(currMax13, middle5, from, res)
            POS_ITER(currMax14, to5, middle5, res)
            POS_ITER(currMax15, to5, from, res)
      }
   }
   else
   {
      for (; from < orig; ++from, 
         ++to1, ++middle1, 
         ++to2, ++middle2, 
         ++to3, ++middle3, 
         ++to4, ++middle4,
         ++to5, ++middle5)
      {
         NEG_ITER(currMax1, middle1, from, res)
         NEG_ITER(currMax2, to1, middle1, res)
         NEG_ITER(currMax3, to1, from, res)
         NEG_ITER(currMax4, middle2, from, res)
         NEG_ITER(currMax5, to2, middle2, res)
         NEG_ITER(currMax6, to2, from, res)
         NEG_ITER(currMax7, middle3, from, res)
         NEG_ITER(currMax8, to3, middle3, res)
         NEG_ITER(currMax9, to3, from, res)
         NEG_ITER(currMax10, middle4, from, res)
         NEG_ITER(currMax11, to4, middle4, res)
         NEG_ITER(currMax12, to4, from, res)
         NEG_ITER(currMax13, middle5, from, res)
         NEG_ITER(currMax14, to5, middle5, res)
         NEG_ITER(currMax15, to5, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}


void calcColumnPairedCombined3x2(int * mat, int M, int fromIndex, short* width, ResultStruct & result)
{
   int* from = &mat[(fromIndex - 1) * M];
   int * orig = from + M;
   int * to1 = &mat[(fromIndex + width[0] + width[1] + width[2] - 1) * M];
   int* middle11 = &mat[(fromIndex + width[0] - 1) * M];
   int* middle12 = &mat[(fromIndex + width[1] + width[1] - 1) * M];
   int * to2 = &mat[(fromIndex + width[3] + width[4] + width[5] - 1) * M];
   int* middle21 = &mat[(fromIndex + width[3] - 1) * M];
   int* middle22 = &mat[(fromIndex + width[3] + width[4] - 1) * M];
   TempResultStruct currMax1(middle11, middle11, width[0], 0);
   TempResultStruct currMax2(middle12, middle12, width[1], 0);
   TempResultStruct currMax3(middle12, middle12, width[0] + width[1], 0);
   TempResultStruct currMax4(to1, to1, width[2], 0);
   TempResultStruct currMax5(to1, to1, width[1] + width[2], 0);
   TempResultStruct currMax6(to1, to1, width[0] + width[1] + width[0], 0);
   TempResultStruct currMax7(middle21, middle21, width[3], 0);
   TempResultStruct currMax8(middle22, middle22, width[4], 0);
   TempResultStruct currMax9(middle22, middle22, width[3] + width[4], 0);
   TempResultStruct currMax10(to2, to2, width[4], 0);
   TempResultStruct currMax11(to2, to2, width[4] + width[5], 0);
   TempResultStruct currMax12(to2, to2, width[3] + width[4] + width[5], 0);
   TempResultStruct res(0, 0, 0, result.m_sum);
   if (res.m_sum > 0)
   {
      for (; from < orig; ++from, ++to1, ++middle11, ++middle12, ++to2, ++middle21, ++middle22)
      {
         POS_ITER(currMax1, middle11, from, res)
         POS_ITER(currMax2, middle12, middle11, res)
         POS_ITER(currMax3, middle12, from, res)
         POS_ITER(currMax4, to1, middle12, res)
         POS_ITER(currMax5, to1, middle11, res)
         POS_ITER(currMax6, to1, from, res)
         POS_ITER(currMax7, middle21, from, res)
         POS_ITER(currMax8, middle22, middle21, res)
         POS_ITER(currMax9, middle22, from, res)
         POS_ITER(currMax10, to2, middle22, res)
         POS_ITER(currMax11, to2, middle21, res)
         POS_ITER(currMax12, to2, from, res)
      }
   }
   else
   {
      for (; from < orig; ++from, ++to1, ++middle11, ++middle12, ++to2, ++middle21, ++middle22)
      {
         NEG_ITER(currMax1, middle11, from, res)
         NEG_ITER(currMax2, middle12, middle11, res)
         NEG_ITER(currMax3, middle12, from, res)
         NEG_ITER(currMax4, to1, middle12, res)
         NEG_ITER(currMax5, to1, middle11, res)
         NEG_ITER(currMax6, to1, from, res)
         NEG_ITER(currMax7, middle21, from, res)
         NEG_ITER(currMax8, middle22, middle21, res)
         NEG_ITER(currMax9, middle22, from, res)
         NEG_ITER(currMax10, to2, middle22, res)
         NEG_ITER(currMax11, to2, middle21, res)
         NEG_ITER(currMax12, to2, from, res)
      }
   }
   if (res.m_sum > result.m_sum)
   {
      result.m_sum = res.m_sum;
      result.m_brColumn = (res.m_beginPtr - mat) / M;
      result.m_ulColumn = result.m_brColumn - res.m_width + 1;
      result.m_ulRow = (res.m_beginPtr - mat)  % M;
      result.m_brRow = (res.m_endPtr - mat)  % M;
   }
}

void prepareCombined3(int * mat, int M, short* width, ResultStruct & res)
{
    for (int i = 0; i < width[0]; ++i)
    {
         calcColumnCombined2(mat, M, i, &width[1], res);
    }
}

void prepareCombined4(int * mat, int M, short* width, ResultStruct & res)
{
   prepareCombined3(mat, M, &width[1], res);
   for (int i = 0; i < width[0]; ++i)
   {
      calcColumnCombined3(mat, M, i, &width[1], res);
   }
}

void prepareCombined5(int * mat, int M, short* width, ResultStruct & res)
{
   prepareCombined4(mat, M, width + 1, res);
   for (int i = 0; i < width[0]; ++i)
   {
      calcColumnCombined4(mat, M, i, width + 1, res);
   }
}

void prepareCombined6(int * mat, int M, short* width, ResultStruct & res)
{
   prepareCombined5(mat, M, width + 1, res);
   for (int i = 0; i < width[0]; ++i)
   {
      calcColumnCombined5(mat, M, i, width + 1, res);
   }
}

void prepareCombined7(int * mat, int M, short* width, ResultStruct & res)
{
   prepareCombined6(mat, M, width + 1, res);
   for (int i = 0; i < width[0]; ++i)
   {
      calcColumnCombined6(mat, M, i, width + 1, res);
   }
}

void prepareCombined8(int * mat, int M, short* width, ResultStruct & res)
{
   prepareCombined7(mat, M, width + 1, res);
   for (int i = 0; i < width[0]; ++i)
   {
      calcColumnCombined7(mat, M, i, width + 1, res);
   }
}

void finalizeCombined3(int * mat, int M, int N, short* width, ResultStruct & res)
{
   for (int i = N - width[2] - width[1] - width[0] + 1; i < N - width[0] - width[1] + 1; ++i)
   {
       calcColumnCombined2(mat, M, i, width, res);
   }
}

void finalizeCombined4(int * mat, int M, int N, short* width, ResultStruct & res)
{
   for (int i = N - width[3] - width[2] - width[1] - width[0] + 1; i < N - width[0] - width[1] - width[2] + 1; ++i)
   {
      calcColumnCombined3(mat, M, i, width, res);
   }
   finalizeCombined3(mat, M, N, width, res);
}

void finalizeCombined5(int * mat, int M, int N, short* width, ResultStruct & res)
{
   for (int i = N - width[4] - width[3] - width[2] - width[1] - width[0] + 1; i < N - width[0] - width[1] - width[2] - width[3] + 1; ++i)
   {
      calcColumnCombined4(mat, M, i, width, res);
   }
   finalizeCombined4(mat, M, N, width, res);
}

void finalizeCombined6(int * mat, int M, int N, short* width, ResultStruct & res)
{
   for (int i = N - width[5] - width[4] - width[3] - width[2] - width[1] - width[0] + 1; i < N - width[0] - width[1] - width[2] - width[3] - width[4] + 1; ++i)
   {
      calcColumnCombined5(mat, M, i, width, res);
   }
   finalizeCombined5(mat, M, N, width, res);
}

void finalizeCombined7(int * mat, int M, int N, short* width, ResultStruct & res)
{
   for (int i = N - width[6] - width[5] - width[4] - width[3] - width[2] - width[1] - width[0] + 1; i < N - width[0] - width[1] - width[2] - width[3] - width[4] - width[5] + 1; ++i)
   {
      calcColumnCombined6(mat, M, i, width, res);
   }
   finalizeCombined6(mat, M, N, width, res);
}


void finalizeCombined8(int * mat, int M, int N, short* width, ResultStruct & res)
{
   for (int i = N - width[7] -width[6] - width[5] - width[4] - width[3] - width[2] - width[1] - width[0] + 1; i < N - width[0] - width[1] - width[2] - width[3] - width[4] - width[5] - width[6] + 1; ++i)
   {
      calcColumnCombined7(mat, M, i, width, res);
   }
   finalizeCombined7(mat, M, N, width, res);
}

void verticalCollectSingleUp(int threadNumber, Task& task)
{
   for (int i = 0 ; i < task.m_columnsCount; ++i)
   {
      for (int j = 0; j < task.m_columnsCount - i - 1; ++j)
      {
          if (task.m_columns[j] > task.m_columns[j + 1])
          {
             short temp = task.m_columns[j];
             task.m_columns[j] = task.m_columns[j + 1];
             task.m_columns[j + 1] = temp;
             temp = task.m_columns[j + 5];
             task.m_columns[j + 5] = task.m_columns[j + 6];
             task.m_columns[j + 6] = temp;
          }
      }
   }
   int N = testCases[task.m_testCase].N;
   int M = testCases[task.m_testCase].M;
   ResultStruct& res = threadsContext[threadNumber].m_testCasesResults[task.m_testCase];
   int * mat = &matrixes[testCases[task.m_testCase].matrixNumber][0] + M;// "zero" column
   int taskInc = task.m_columnsCount;
   switch(task.m_columnsCount)
   {
   case 1:
   {
      for (int i = 0; i < task.m_columns[0]; ++i)
      {
         calcColumn(mat, M, i, task.m_columns[5], res);
      }
      break;
   }
   case 2:
   {
      for (int i = 0; i < task.m_columns[0]; ++i)
      {
         calcColumnPaired2(mat, M, i, task.m_columns + 5, res);
      }
      for (int i = task.m_columns[0]; i < task.m_columns[1]; ++i)
      {
         calcColumn(mat, M, i, task.m_columns[6], res);
      }
      break;
   }
   case 3:
   {
      for (int i = 0; i < task.m_columns[0]; ++i)
      {
         calcColumnPaired3(mat, M, i, task.m_columns + 5, res);
      }
      for (int i = task.m_columns[0]; i < task.m_columns[1]; ++i)
      {
         calcColumnPaired2(mat, M, i, task.m_columns + 6, res);
      }
      for (int i = task.m_columns[1]; i < task.m_columns[2]; ++i)
      {
         calcColumn(mat, M, i, task.m_columns[7], res);
      }
      break;
   }
   case 4:
   {
      for (int i = 0; i < task.m_columns[0]; ++i)
      {
         calcColumnPaired4(mat, M, i, task.m_columns + 5, res);
      }
      for (int i = task.m_columns[0]; i < task.m_columns[1]; ++i)
      {
         calcColumnPaired3(mat, M, i, task.m_columns + 6, res);
      }
      for (int i = task.m_columns[1]; i < task.m_columns[2]; ++i)
      {
         calcColumnPaired2(mat, M, i, task.m_columns + 7, res);
      }
      for (int i = task.m_columns[2]; i < task.m_columns[3]; ++i)
      {
         calcColumn(mat, M, i, task.m_columns[8], res);
      }
      break;
   }
   case 5:
   {
      for (int i = 0; i < task.m_columns[0]; ++i)
      {
         calcColumnPaired5(mat, M, i, task.m_columns + 5, res);
      }
      for (int i = task.m_columns[0]; i < task.m_columns[1]; ++i)
      {
         calcColumnPaired4(mat, M, i, task.m_columns + 6, res);
      }
      for (int i = task.m_columns[1]; i < task.m_columns[2]; ++i)
      {
         calcColumnPaired3(mat, M, i, task.m_columns + 7, res);
      }
      for (int i = task.m_columns[2]; i < task.m_columns[3]; ++i)
      {
         calcColumnPaired2(mat, M, i, task.m_columns + 8, res);
      }
      for (int i = task.m_columns[3]; i < task.m_columns[4]; ++i)
      {
         calcColumn(mat, M, i, task.m_columns[9], res);
      }
      break;   
   }
   };
   pthread_spin_lock(&testCases[task.m_testCase].m_lock);
   testCases[task.m_testCase].countedColumnSet += taskInc;
   if (testCases[task.m_testCase].countedColumnSet < N)
   {
      pthread_spin_unlock(&testCases[task.m_testCase].m_lock);
      return;
   }
   pthread_spin_unlock(&testCases[task.m_testCase].m_lock);
   pthread_spin_lock(&taskCountDoneLock);
   ++casesDone;
   if (casesDone == T)
   {
      workDone = true;
      pthread_spin_unlock(&taskCountDoneLock);
   }
   pthread_spin_unlock(&taskCountDoneLock);
   Task newTask;
   newTask.m_func = &initialTask;
   newTask.m_columnsCount = testCases[task.m_testCase].matrixNumber;
   threadsContext[threadNumber].m_tasks.push(newTask);
}

void verticalCollectSingleDown(int threadNumber, Task& task)
{
    for (int i = 0 ; i < task.m_columnsCount; ++i)
    {
       for (int j = 0; j < task.m_columnsCount - i - 1; ++j)
       {
          if (task.m_columns[j] > task.m_columns[j + 1])
          {
             short temp = task.m_columns[j];
             task.m_columns[j] = task.m_columns[j + 1];
             task.m_columns[j + 1] = temp;
             temp = task.m_columns[j + 5];
             task.m_columns[j + 5] = task.m_columns[j + 6];
             task.m_columns[j + 6] = temp;
          }
       }
    }
    int N = testCases[task.m_testCase].N;
    int M = testCases[task.m_testCase].M;
    ResultStruct& res = threadsContext[threadNumber].m_testCasesResults[task.m_testCase];
    int * mat = &matrixes[testCases[task.m_testCase].matrixNumber][0] + M;// "zero" column
    int taskInc = task.m_columnsCount;
    switch(task.m_columnsCount)
    {
    case 1:
    {
       for (int i = N - 1; i >= N - task.m_columns[5]; --i)
       {
          calcColumn(mat, M, i - task.m_columns[0] + 1, task.m_columns[0], res);
       }
       break;
    }
    case 2:
    {
       for (int i = N - 1; i >= N - task.m_columns[5]; --i)
       {
          calcColumnPairedTo2(mat, M, i, task.m_columns, res);
       }
       for (int i = N - task.m_columns[5] - 1; i >= N - task.m_columns[6]; --i)
       {
          calcColumn(mat, M, i - task.m_columns[1] + 1, task.m_columns[1], res);
       }
       break;
    }
    case 3:
    {
       for (int i = N - 1; i >= N - task.m_columns[5]; --i)
       {
          calcColumnPairedTo3(mat, M, i, task.m_columns, res);
       }
       for (int i = N - task.m_columns[5] - 1; i >= N - task.m_columns[6]; --i)
       {
          calcColumnPairedTo2(mat, M, i, task.m_columns + 1, res);
       }
       for (int i = N - task.m_columns[6] - 1; i >= N - task.m_columns[7]; --i)
       {
          calcColumn(mat, M, i - task.m_columns[2] + 1, task.m_columns[2], res);
       }
       break;
    }
    case 4:
    {
       for (int i = N - 1; i >= N - task.m_columns[5]; --i)
       {
          calcColumnPairedTo4(mat, M, i, task.m_columns, res);
       }
       for (int i = N - task.m_columns[5] - 1; i >= N - task.m_columns[6]; --i)
       {
          calcColumnPairedTo3(mat, M, i, task.m_columns + 1, res);
       }
       for (int i = N - task.m_columns[6] - 1; i >= N - task.m_columns[7]; --i)
       {
          calcColumnPairedTo2(mat, M, i, task.m_columns + 2, res);
       }
       for (int i = N - task.m_columns[7] - 1; i >= N - task.m_columns[8]; --i)
       {
          calcColumn(mat, M, i - task.m_columns[3] + 1, task.m_columns[3], res);
       }
       break;
    }
    case 5:
    {
       for (int i = N - 1; i >= N - task.m_columns[5]; --i)
       {
          calcColumnPairedTo5(mat, M, i, task.m_columns, res);
       }
       for (int i = N - task.m_columns[5] - 1; i >= N - task.m_columns[6]; --i)
       {
          calcColumnPairedTo4(mat, M, i, task.m_columns + 1, res);
       }
       for (int i = N - task.m_columns[6] - 1; i >= N - task.m_columns[7]; --i)
       {
          calcColumnPairedTo3(mat, M, i, task.m_columns + 2, res);
       }
       for (int i = N - task.m_columns[7] - 1; i >= N - task.m_columns[8]; --i)
       {
          calcColumnPairedTo2(mat, M, i, task.m_columns + 3, res);
       }
       for (int i = N - task.m_columns[8] - 1; i >= N - task.m_columns[9]; --i)
       {
          calcColumn(mat, M, i - task.m_columns[4] + 1, task.m_columns[4], res);
       }
       break;
    }
    };
    pthread_spin_lock(&testCases[task.m_testCase].m_lock);
    testCases[task.m_testCase].countedColumnSet += taskInc;
    if (testCases[task.m_testCase].countedColumnSet < N)
    {
       pthread_spin_unlock(&testCases[task.m_testCase].m_lock);
       return;
    }
    pthread_spin_unlock(&testCases[task.m_testCase].m_lock);
    pthread_spin_lock(&taskCountDoneLock);
    ++casesDone;
    if (casesDone == T)
    {
       workDone = true;
       pthread_spin_unlock(&taskCountDoneLock);
    }
    pthread_spin_unlock(&taskCountDoneLock);
    Task newTask;
    newTask.m_func = &initialTask;
    newTask.m_columnsCount = testCases[task.m_testCase].matrixNumber;
    threadsContext[threadNumber].m_tasks.push(newTask);
}

//TASK1
void verticalTask(int threadNumber, Task& task)
{
    int N = testCases[task.m_testCase].N;
    int M = testCases[task.m_testCase].M;
    ResultStruct& res = threadsContext[threadNumber].m_testCasesResults[task.m_testCase];
    int * mat = &matrixes[testCases[task.m_testCase].matrixNumber][0] + M;// "zero" column
    int taskInc = 0;
    switch (task.m_columnTaskType)
    {
    case Task::COMBINED:
    {
        switch (task.m_columnsCount)
        {
        case 2:
        {
            taskInc = 1;
            for (int i = 0; i < N - task.m_columns[0] - task.m_columns[2] + 1; ++i)
            {
               calcColumnCombined2(mat, M, i, task.m_columns, res);
            }
            break;
        }
        case 3:
        {
            taskInc = 4;
            prepareCombined3(mat, M, task.m_columns, res);
            for (int i = 0; i < N - task.m_columns[0] - task.m_columns[1] - task.m_columns[2] + 1; ++i)
            {
               calcColumnCombined3(mat, M, i, task.m_columns, res);
            }
            finalizeCombined3(mat, M, N, task.m_columns, res);
            break;
        }
        case 4:
           {
              taskInc = 8;
              prepareCombined4(mat, M, task.m_columns, res);
              for (int i = 0; i < N - task.m_columns[0] - task.m_columns[1] - task.m_columns[2] - task.m_columns[3] + 1; ++i)
              {
                 calcColumnCombined4(mat, M, i, task.m_columns, res);
              }
              finalizeCombined4(mat, M, N, task.m_columns, res);
              break;
           }
        case 5:
           {
              taskInc = 13;
              prepareCombined5(mat, M, task.m_columns, res);
              for (int i = 0; i < N - task.m_columns[0] - task.m_columns[1] - task.m_columns[2] - task.m_columns[3] - task.m_columns[4] + 1; ++i)
              {
                 calcColumnCombined5(mat, M, i, task.m_columns, res);
              }
              finalizeCombined5(mat, M, N, task.m_columns, res);
              break;
           }
        case 6:
           {
              taskInc = 19;
              prepareCombined6(mat, M, task.m_columns, res);
              for (int i = 0; i < N - task.m_columns[0] - task.m_columns[1] - task.m_columns[2] - task.m_columns[3] - task.m_columns[4] - task.m_columns[5] + 1; ++i)
              {
                 calcColumnCombined6(mat, M, i, task.m_columns, res);
              }
              finalizeCombined6(mat, M, N, task.m_columns, res);
              break;
           }
        case 7:
           {
              taskInc = 26;
              prepareCombined7(mat, M, task.m_columns, res);
              for (int i = 0; i < N - task.m_columns[0] - task.m_columns[1] - task.m_columns[2] - task.m_columns[3] - task.m_columns[4] - task.m_columns[5] - task.m_columns[6] + 1; ++i)
              {
                 calcColumnCombined7(mat, M, i, task.m_columns, res);
              }
              finalizeCombined7(mat, M, N, task.m_columns, res);
              break;
           }
        case 8:
           {
              taskInc = 34;
              prepareCombined8(mat, M, task.m_columns, res);
              for (int i = 0; i < N - task.m_columns[0] - task.m_columns[1] - task.m_columns[2] - task.m_columns[3] - task.m_columns[4] - task.m_columns[5] - task.m_columns[6] - task.m_columns[7] + 1; ++i)
              {
                 calcColumnCombined8(mat, M, i, task.m_columns, res);
              }
              finalizeCombined8(mat, M, N, task.m_columns, res);
              break;
           }
        };
        
        break;
    }
    case Task::PAIRED:
    {
        taskInc = 6;
        for (int i = 0; i < N - task.m_columns[5] + 1; ++i)
        {
           calcColumnPaired6(mat, M, i, task.m_columns, res);
        }
        for (int i = N - task.m_columns[5] + 1; i < N - task.m_columns[4] + 1; ++i)
        {
           calcColumnPaired5(mat, M, i, task.m_columns, res);
        }
        for (int i = N - task.m_columns[4] + 1; i < N - task.m_columns[3] + 1; ++i)
        {
           calcColumnPaired4(mat, M, i, task.m_columns, res);
        }
        for (int i = N - task.m_columns[3] + 1; i < N - task.m_columns[2] + 1; ++i)
        {
           calcColumnPaired3(mat, M, i, task.m_columns, res);
        }
        for (int i = N - task.m_columns[2] + 1; i < N - task.m_columns[1] + 1; ++i)
        {
           calcColumnPaired2(mat, M, i, task.m_columns, res);
        }
        for (int i = N - task.m_columns[1] + 1; i < N - task.m_columns[0] + 1; ++i)
        {
           calcColumn(mat, M, i, task.m_columns[0], res);
        }
        break;
    }
    case Task::SINGLE:
    {
        taskInc = 1;
        for (int i = 0; i < N - task.m_columns[0] + 1; ++i)
        {
           calcColumn(mat, M, i, task.m_columns[0], res);
        }
        break;
    }
	   case Task::PAIRED_COMBINED:
    {
        int groupsCount = task.m_columnsCount / 10;
        int groupPower = task.m_columnsCount % 10;
        switch (groupPower)
        {
            case 2:
            {
               switch(groupsCount)
               {
                  case 5:
                  {
                     for (int i = N - task.m_columns[8] - task.m_columns[9] + 1; i <= N - task.m_columns[6] - task.m_columns[7]; ++i)
                     {
                        calcColumnPairedCombined2x4(mat, M, i, task.m_columns, res);   
                     }
                  }
                  case 4:
                  {
                     for (int i = N - task.m_columns[6] - task.m_columns[7] + 1; i <= N - task.m_columns[4] - task.m_columns[5]; ++i)
                     {
                        calcColumnPairedCombined2x3(mat, M, i, task.m_columns, res);   
                     }
                  }
                  case 3:
                  {
                     for (int i = N - task.m_columns[4] - task.m_columns[5] + 1; i <= N - task.m_columns[2] - task.m_columns[3]; ++i)
                     {
                        calcColumnPairedCombined2x2(mat, M, i, task.m_columns, res);   
                     }
                     for (int i = N - task.m_columns[2] - task.m_columns[3] + 1; i <= N - task.m_columns[0] - task.m_columns[1]; ++i)
                     {
                        calcColumnCombined2(mat, M, i, task.m_columns, res);
                     }
                  }
               }
               switch (groupsCount)
               {
                  case 3:
                  {
                     taskInc = 3;
                     for (int i = 0; i <= N - task.m_columns[4] - task.m_columns[5]; ++i)
                     {
                        calcColumnPairedCombined2x3(mat, M, i, task.m_columns, res);   
                     }
                     break;
                  }
                  case 4:
                  {
                     taskInc = 4;
                     for (int i = 0; i <= N - task.m_columns[6] - task.m_columns[7]; ++i)
                     {
                        calcColumnPairedCombined2x4(mat, M, i, task.m_columns, res);   
                     }
                     break;
                  }
                  case 5:
                  {
                     taskInc = 5;
                     for (int i = 0; i <= N - task.m_columns[8] - task.m_columns[9]; ++i)
                     {
                        calcColumnPairedCombined2x5(mat, M, i, task.m_columns, res);   
                     }
                     break;
                  }
               }
               break;
            }
            case 3:
            {
               taskInc = 8;
               prepareCombined3(mat, M, task.m_columns, res);
               prepareCombined3(mat, M, task.m_columns + 3, res);
               for (int i = 0; i <= N - task.m_columns[3] - task.m_columns[4] - task.m_columns[5]; ++i)
               {
                  calcColumnPairedCombined3x2(mat, M, i, task.m_columns, res);
               }
               for (int i = N  - task.m_columns[3] - task.m_columns[4] - task.m_columns[5] + 1; i <= N - task.m_columns[0] - task.m_columns[1] - task.m_columns[2]; ++i)
               {
                  calcColumnCombined3(mat, M, i, task.m_columns, res);
               }
               finalizeCombined3(mat, M, N, task.m_columns, res);
               finalizeCombined3(mat, M, N, task.m_columns + 3, res);
               break;
            }
        }
        break;
    }
    };
    pthread_spin_lock(&testCases[task.m_testCase].m_lock);
    testCases[task.m_testCase].countedColumnSet += taskInc;
    if (testCases[task.m_testCase].countedColumnSet < N)
    {
        pthread_spin_unlock(&testCases[task.m_testCase].m_lock);
        return;
    }
    pthread_spin_unlock(&testCases[task.m_testCase].m_lock);
    pthread_spin_lock(&taskCountDoneLock);
    ++casesDone;
    if (casesDone == T)
    {
        workDone = true;
        pthread_spin_unlock(&taskCountDoneLock);
    }
    pthread_spin_unlock(&taskCountDoneLock);
    Task newTask;
    newTask.m_func = &initialTask;
    newTask.m_columnsCount = testCases[task.m_testCase].matrixNumber;
    threadsContext[threadNumber].m_tasks.push(newTask);
}

void createPairedCombinedColumnTask(int threadNumber, int testCase, int* busy, int N, int groupPower, int pairedPower, Task& collectSingleUp, Task& collectSingleDown)
{
   int foundeGroups = 0;
   int founded = 0;
   short p[10][10] = {0};
   int sum = 0;
   Task newTask;
   newTask.m_func = &verticalTask;
   newTask.m_testCase = testCase;
   newTask.m_columnsCount = pairedPower * 10 + groupPower;
   newTask.m_columnTaskType = Task::PAIRED_COMBINED;
   for (int i = 1; (i <= N) && (sum + i <= N); ++i)
   {
      if (busy[i] == 1) continue;
      bool sut = true;
      for (int p1 = 0; (p1 < founded - 1) && (sut); ++p1)
      {
         int ps = p[foundeGroups][p1];
         for (int p2 = p1 + 1; (p2 < founded) && (sut); ++p2)
         {
            ps += p[foundeGroups][p2];
            if (ps == i)
            {
               sut = false;
            }
         }
      }
      if (!sut) continue;
      int ps = i;
      int j;
      for (j = founded - 1; (j >= 0) && (sut); --j)
      {
         if (busy[ps + p[foundeGroups][j]] == 1)
         {
            sut = false;
            break;
         }
         ps += p[foundeGroups][j];
      }
      if (!sut) continue;
      p[foundeGroups][founded] = i;
      ++founded;
      sum += i;
      if (founded == groupPower)
      {
         for (int j = 0; j < groupPower; ++j)
         {
            busy[p[foundeGroups][j]] = 1;
         }
         for (int p1 = 0; p1 < founded - 1; ++p1)
         {
            int ps = p[foundeGroups][p1];
            for (int p2 = p1 + 1; p2 < founded; ++p2)
            {
               ps += p[foundeGroups][p2];
               busy[ps] = 1;
            }
         }
         ++foundeGroups;
         if (foundeGroups == pairedPower)
         {
            int k = 0;
            for (int p1 = 0; p1 < foundeGroups; ++p1)
            {
               for (int p2 = 0; p2 < founded; ++p2)
               {
                  newTask.m_columns[k] = p[p1][p2];
                  ++k;
               }
               collectSingleUp.m_columns[collectSingleUp.m_columnsCount] = p[p1][groupPower - 2];
               collectSingleUp.m_columns[collectSingleUp.m_columnsCount + 5] = p[p1][groupPower - 1];
               ++collectSingleUp.m_columnsCount;
               if (collectSingleUp.m_columnsCount == 5)
               {
                  threadsContext[threadNumber].m_tasks.push(collectSingleUp);
                  collectSingleUp.m_columnsCount = 0;
               }
               collectSingleDown.m_columns[collectSingleDown.m_columnsCount] = p[p1][0];
               collectSingleDown.m_columns[collectSingleDown.m_columnsCount + 5] = p[p1][1];
               ++collectSingleDown.m_columnsCount;
               if (collectSingleDown.m_columnsCount == 5)
               {
                  threadsContext[threadNumber].m_tasks.push(collectSingleDown);
                  collectSingleDown.m_columnsCount = 0;
               }
            }
            threadsContext[threadNumber].m_tasks.push(newTask);
            foundeGroups = 0;
         }
         founded = 0;
         sum = 0;
      }
   }
   while (foundeGroups != 0)
   {
      for (int j = 0; j < groupPower; ++j)
      {
         busy[p[foundeGroups - 1][j]] = 0;
      }
      for (int p1 = 0; p1 < groupPower - 1; ++p1)
      {
         int ps = p[foundeGroups - 1][p1];
         for (int p2 = p1 + 1; p2 < groupPower; ++p2)
         {
            ps += p[foundeGroups - 1][p2];
            busy[ps] = 0;
         }
      }
      --foundeGroups;
   }
}

inline void createCombinedColumnTask(int threadNumber, int testCase, int* busy, int N, int groupPower, Task& collectSingleUp, Task& collectSingleDown)
{
   int founded = 0;
   short p[10] = {0};
   int sum = 0;
   Task newTask;
   newTask.m_func = &verticalTask;
   newTask.m_testCase = testCase;
   newTask.m_columnTaskType = Task::COMBINED;
   for (int i = 1; (i <= N) && (sum + i <= N); ++i)
   {
      if (busy[i] == 1) continue;
      bool sut = true;
      for (int p1 = 0; (p1 < founded - 1) && (sut); ++p1)
      {
          int ps = p[p1];
          for (int p2 = p1 + 1; (p2 < founded) && (sut); ++p2)
          {
              ps += p[p2];
              if (ps == i)
              {
                  sut = false;
              }
          }
      }
      if (!sut) continue;
      int ps = i;
      int j;
      for (j = founded - 1; (j >= 0) && (sut); --j)
      {
          if (busy[ps + p[j]] == 1)
          {
              sut = false;
              break;
          }
          ps += p[j];
      }
      if (!sut) continue;
      p[founded] = i;
      ++founded;
      sum += i;
      if (founded == groupPower)
      {
         memset(&newTask.m_columns[0], 0, sizeof(newTask.m_columns));
         newTask.m_columnsCount = groupPower;
         memcpy(&newTask.m_columns[0], &p[0], sizeof(newTask.m_columns[0]) * groupPower);
         threadsContext[threadNumber].m_tasks.push(newTask);
         for (int j = 0; j < groupPower; ++j)
         {
            busy[p[j]] = 1;
         }
         for (int p1 = 0; p1 < founded - 1; ++p1)
         {
            int ps = p[p1];
            for (int p2 = p1 + 1; p2 < founded; ++p2)
            {
               ps += p[p2];
               busy[ps] = 1;
            }
         }
         collectSingleUp.m_columns[collectSingleUp.m_columnsCount] = p[groupPower - 2];
         collectSingleUp.m_columns[collectSingleUp.m_columnsCount + 5] = p[groupPower - 1];
         ++collectSingleUp.m_columnsCount;
         if (collectSingleUp.m_columnsCount == 5)
         {
            threadsContext[threadNumber].m_tasks.push(collectSingleUp);
            collectSingleUp.m_columnsCount = 0;
         }
         collectSingleDown.m_columns[collectSingleDown.m_columnsCount] = p[0];
         collectSingleDown.m_columns[collectSingleDown.m_columnsCount + 5] = p[1];
         ++collectSingleDown.m_columnsCount;
         if (collectSingleDown.m_columnsCount == 5)
         {
            threadsContext[threadNumber].m_tasks.push(collectSingleDown);
            collectSingleDown.m_columnsCount = 0;
         }
         founded = 0;
         sum = 0;
      }
   }
}


inline void createPairedColumnTask(int threadNumber, int testCase, int* busy, int N, int groupPower)
{
   int founded = 0;
   short p[10] = {0};
   Task newTask;
   newTask.m_func = &verticalTask;
   newTask.m_testCase = testCase;
   newTask.m_columnTaskType = Task::PAIRED;
   for (int i = 1; i <= N; ++i)
   {
      if (busy[i] == 1) continue;
      p[founded] = i;
      ++founded;
      if (founded == groupPower)
      {
         memset(&newTask.m_columns[0], 0, sizeof(newTask.m_columns));
         newTask.m_columnsCount = groupPower;
         memcpy(&newTask.m_columns[0], &p[0], sizeof(short) * groupPower);
         threadsContext[threadNumber].m_tasks.push(newTask);
         for (int j = 0; j < groupPower; ++j)
         {
            busy[p[j]] = 1;
         }
         founded = 0;
      }
   }
   newTask.m_columnTaskType = Task::SINGLE;
   newTask.m_columnsCount = 1;
   for (int i = 0; i < founded; ++i)
   {
      newTask.m_columns[0] = p[i];
      threadsContext[threadNumber].m_tasks.push(newTask);  
   }
}

//TASK 0
void fillMatrixTask(int threadNumber, Task& task)
{
    int *matrix = matrixes[testCases[task.m_testCase].matrixNumber];
    testCases[task.m_testCase].isTrans = testCases[task.m_testCase].M < testCases[task.m_testCase].N;
    bool isTrans = testCases[task.m_testCase].isTrans;
    if (isTrans)
    {
        int tmp = testCases[task.m_testCase].M;
        testCases[task.m_testCase].M = testCases[task.m_testCase].N;
        testCases[task.m_testCase].N = tmp;
    }
    memset(matrix, 0, testCases[task.m_testCase].M * sizeof(int));
    matrix += testCases[task.m_testCase].M;

    int a = testCases[task.m_testCase].a;
    int b = testCases[task.m_testCase].b;
    int m = testCases[task.m_testCase].m;

    int M = testCases[task.m_testCase].M;
    int N = testCases[task.m_testCase].N;

    int dstSize = M * N;
    int seed = testCases[task.m_testCase].seed0;
    int i, j;
    long long sum = 0;
    int mean, remainder;

    long long rowSum;
        
    if (isTrans)
    {
        for (j = 0; j < M; ++j)
        {
            seed = (a * seed + b) % m;
            matrix[j] = seed;
            sum += seed;
        }

        for (i = 1; i < N; ++i)
        {
            for (j = 0; j < M; ++j)
            {
                seed = (a * seed + b) % m;
                matrix[i * M + j] = matrix[(i-1) * M + j] + seed;
                sum += seed;
            }
        }

    }
    else
    {
        for (i = 0; i < M; ++i)
        {
            rowSum = 0;
            for (j = 0; j < N; ++j)
            {
                seed = (a * seed + b) % m;
                rowSum += seed;
                matrix[j * M + i] = rowSum;
                sum += seed;
            }
        }
    }
    /* calculate the mean value. Avoid float logic when making rounding. */
    mean = (int) (sum / (long long) dstSize); /* updated line */
    remainder = (int) (sum % (long long) dstSize); /* updated line */
    mean += (remainder * 2 > (signed) dstSize) ? (1) : (0); /* updated line */
    
    for (j = 0; j < N; ++j)
    {
        for (i = 0; i < M; ++i)
        {

            {matrix[j * M + i] -= mean * (j + 1);}
        }
    }
    int busy[20001] = {0};
    Task collectSingleUp;
    collectSingleUp.m_columnsCount = 0;
    collectSingleUp.m_testCase = task.m_testCase;
    collectSingleUp.m_func = &verticalCollectSingleUp;
    Task collectSingleDown;
    collectSingleDown.m_columnsCount = 0;
    collectSingleDown.m_testCase = task.m_testCase;
    collectSingleDown.m_func = &verticalCollectSingleDown;
    createCombinedColumnTask(threadNumber, task.m_testCase, busy, N, 8, collectSingleUp, collectSingleDown);
    createCombinedColumnTask(threadNumber, task.m_testCase, busy, N, 7, collectSingleUp, collectSingleDown);
    createCombinedColumnTask(threadNumber, task.m_testCase, busy, N, 6, collectSingleUp, collectSingleDown);
    createCombinedColumnTask(threadNumber, task.m_testCase, busy, N, 5, collectSingleUp, collectSingleDown);
    createCombinedColumnTask(threadNumber, task.m_testCase, busy, N, 4, collectSingleUp, collectSingleDown);
    //createPairedCombinedColumnTask(threadNumber, task.m_testCase, busy, N, 3, 2, collectSingleUp, collectSingleDown);
    createPairedCombinedColumnTask(threadNumber, task.m_testCase, busy, N, 2, 5, collectSingleUp, collectSingleDown);
    createPairedColumnTask(threadNumber, task.m_testCase, busy, N, 6);
    if (collectSingleUp.m_columnsCount != 0)
    {
       threadsContext[threadNumber].m_tasks.push(collectSingleUp);
    }
    if (collectSingleDown.m_columnsCount != 0)
    {
       threadsContext[threadNumber].m_tasks.push(collectSingleDown);
    }
}

// TASK -1
void initialTask(int threadNumber, Task& task)
{
    pthread_spin_lock(&taskCountLock);
    if (casesLaunched == T)
    {
        pthread_spin_unlock(&taskCountLock);
        return;
    }
    Task nextTask;
    nextTask.m_func = &fillMatrixTask;
    nextTask.m_testCase = casesLaunched;
    ++casesLaunched;
    pthread_spin_unlock(&taskCountLock);
    testCases[nextTask.m_testCase].matrixNumber = task.m_columnsCount;;
    if (matrixes[testCases[nextTask.m_testCase].matrixNumber] == 0)
    {
        matrixes[testCases[nextTask.m_testCase].matrixNumber] = new int[DATA_SIZE];
    }
    testCases[nextTask.m_testCase].countedColumnSet = 0;
    threadsContext[threadNumber].m_tasks.push(nextTask);
}

bool getTask(int threadNumber, Task& task)
{
    if (threadsContext[threadNumber].m_tasks.pop(task))
    {
        return true;
    }
    //this can be optimized if we first try to steal from thread on same processor
    int i = (threadNumber + 1) % numCores;
    while (!workDone)
    {
        if (threadsContext[i].m_tasks.pop(task))
        {
            return true;
        }
        i = (i + 1) % numCores;
    }
    return false;
}

void* threadRoutine(void* params)
{
    int threadNumber = *((int*)params);
    Task task;
    while (!workDone)
    {
        if (getTask(threadNumber, task))
        {
            task.m_func(threadNumber, task);
        }
    }
    return 0;
}

int main(int argc, char * argv[])
{
    int nums[MAX_THREAD_NUMBER];
    for (int i = 0; i< MAX_THREAD_NUMBER; ++i)
    {
        nums[i] = i;
    }
    pthread_t thrd[MAX_THREAD_NUMBER];
#if defined(WIN32) || defined(WIN64)
    LARGE_INTEGER fr, bt, et;
    QueryPerformanceFrequency(&fr);
    QueryPerformanceCounter(&bt);
#endif
    numCores = GetNumberOfCores();
#if defined(KOROL_TEST)
    numCores = 4;
#endif
#if defined(_DEBUG)
    numCores = 1;
#endif
    //current thread is tread number 0;
    for (int i = 1; i < numCores; ++i)
    {
        threadsContext[i].m_number = i;
        pthread_create(&thrd[i], 0, &threadRoutine, &nums[i]);
    }
    if (argc > 1 )
    {
        freopen(argv[1], "rt", stdin);
        freopen(argv[2], "w", stdout);
    } else
    {
        freopen("input.txt", "rt", stdin);
        freopen("output.txt", "w", stdout);
    }
    pthread_spin_init(&taskCountLock, 0);
    pthread_spin_init(&taskCountDoneLock, 0);
    scanf("%d", &T);
    for (int t = 0; t < T; ++t)
    {
        scanf("%d%d%d%d%d%d", &testCases[t].M, &testCases[t].N, &testCases[t].seed0, &testCases[t].a, &testCases[t].b, &testCases[t].m);
        pthread_spin_init(&testCases[t].m_lock, 0);
    }
    Task initTask;
    initTask.m_func = &initialTask;
    if (numCores > 10)
    {
        //its probably some big computer with a lot of memory
        concurrentCases = MAX_CONCURRENT_CASES;
    }
#if defined(KOROL_TEST)
    concurrentCases = 4;
#endif
#if defined(_DEBUG)
    concurrentCases = 1;
#endif
    for (int i = 0; i < concurrentCases; ++i)
    {
        //matrixes[i] = new int[DATA_SIZE];
        initTask.m_columnsCount = i;
        threadsContext[i].m_tasks.push(initTask);
    }
    threadRoutine(&nums[0]);
    for (int i = 0; i < T; ++i)
    {
        int maxN = 0;
        for (int j = 1; j < numCores; ++j)
        {
            if (threadsContext[j].m_testCasesResults[i].m_sum > threadsContext[maxN].m_testCasesResults[i].m_sum)
            {
                maxN = j;
            }
        }
        const ResultStruct &res = threadsContext[maxN].m_testCasesResults[i];
        if (testCases[i].isTrans)
        {
            printf("Case #%d: %d %d %d %d %d %d\n",
              i + 1,
              res.m_ulColumn,
              res.m_ulRow,
              res.m_brColumn,
              res.m_brRow,
              res.m_sum,
              (res.m_brColumn - res.m_ulColumn + 1) * (res.m_brRow - res.m_ulRow + 1));
        }
        else
        {
           printf("Case #%d: %d %d %d %d %d %d\n",
                   i + 1,
                   res.m_ulRow,
                   res.m_ulColumn,
                   res.m_brRow,
                   res.m_brColumn,
                   res.m_sum,
                   (res.m_brColumn - res.m_ulColumn + 1) * (res.m_brRow - res.m_ulRow + 1));
        }
    }
#if defined(WIN32) || defined(WIN64)
    QueryPerformanceCounter(&et);
    printf("time = %ds",(et.QuadPart - bt.QuadPart) / fr.QuadPart);
#endif
    for (int i=0; i < concurrentCases; ++i)
    {
        if (matrixes[i] != 0)
        {
            delete[] matrixes[i];
        }
    }
    return 0;
} // int main(int argc, char * argv[])
